#include "stdafx.h"
#include "net/HTTPSession.hpp"
#include "net/HTTPBufferAllocator.hpp"

NET_NAMESPACE_BEGIN

CHTTPSession::CHTTPSession()
	: m_pBuffer(0),
	m_pCurrent(0),
	m_pEnd(0),
	m_keepAlive(false),
	m_timeout(HTTP_DEFAULT_TIMEOUT),
	m_pException(0)
{
}

CHTTPSession::CHTTPSession(const CStreamSocket& socket)
	: m_socket(socket),
	m_pBuffer(0),
	m_pCurrent(0),
	m_pEnd(0),
	m_keepAlive(false),
	m_timeout(HTTP_DEFAULT_TIMEOUT),
	m_pException(0)
{
}

CHTTPSession::CHTTPSession(const CStreamSocket& socket, bool keepAlive)
	: m_socket(socket),
	m_pBuffer(0),
	m_pCurrent(0),
	m_pEnd(0),
	m_keepAlive(keepAlive),
	m_timeout(HTTP_DEFAULT_TIMEOUT),
	m_pException(0)
{
}

CHTTPSession::~CHTTPSession()
{
	try
	{
		if (m_pBuffer) CHTTPBufferAllocator::deallocate(m_pBuffer, CHTTPBufferAllocator::BUFFER_SIZE);
	}
	catch (...)
	{
		jh_unexpected();
	}
	try
	{
		close();
	}
	catch (...)
	{
	}
	delete m_pException;
}

void CHTTPSession::setKeepAlive(bool keepAlive)
{
	m_keepAlive = keepAlive;
}

bool CHTTPSession::getKeepAlive() const
{
	return m_keepAlive;
}

CStreamSocket& CHTTPSession::socket()
{
	return m_socket;
}

const CException* CHTTPSession::networkException() const
{
	return m_pException;
}

int CHTTPSession::buffered() const
{
	return static_cast<int>(m_pEnd - m_pCurrent);
}

const CAny& CHTTPSession::sessionData() const
{
	return m_data;
}

void CHTTPSession::setTimeout(const CTimeSpan& timeout)
{
	m_timeout = timeout;
}

CTimeSpan CHTTPSession::getTimeout() const
{
	return m_timeout;
}

int CHTTPSession::get()
{
	if (m_pCurrent == m_pEnd)
		refill();

	if (m_pCurrent < m_pEnd)
		return *m_pCurrent++;
	else
		return std::char_traits<char>::eof();
}

int CHTTPSession::peek()
{
	if (m_pCurrent == m_pEnd)
		refill();

	if (m_pCurrent < m_pEnd)
		return *m_pCurrent;
	else
		return std::char_traits<char>::eof();
}

int CHTTPSession::read(char* buffer, std::streamsize length)
{
	if (m_pCurrent < m_pEnd)
	{
		int n = (int) (m_pEnd - m_pCurrent);
		if (n > length) n = (int) length;
		std::memcpy(buffer, m_pCurrent, n);
		m_pCurrent += n;
		return n;
	}
	else return receive(buffer, (int) length);
}

int CHTTPSession::write(const char* buffer, std::streamsize length)
{
	try
	{
		return m_socket.sendBytes(buffer, (int) length);
	}
	catch (CException& exc)
	{
		setException(exc);
		throw;
	}
}

int CHTTPSession::receive(char* buffer, int length)
{
	try
	{
		return m_socket.receiveBytes(buffer, length);
	}
	catch (CException& exc)
	{
		setException(exc);
		throw;
	}
}

void CHTTPSession::refill()
{
	if (!m_pBuffer)
	{
		m_pBuffer = CHTTPBufferAllocator::allocate(CHTTPBufferAllocator::BUFFER_SIZE);
	}
	m_pCurrent = m_pEnd = m_pBuffer;
	int n = receive(m_pBuffer, CHTTPBufferAllocator::BUFFER_SIZE);
	m_pEnd += n;
}

bool CHTTPSession::connected() const
{
	return m_socket.impl()->initialized();
}

void CHTTPSession::connect(const CSocketAddress& address)
{
	m_socket.connect(address, m_timeout);
	m_socket.setReceiveTimeout(m_timeout);
	m_socket.setNoDelay(true);
	// There may be leftover data from a previous (failed) request in the buffer,
	// so we clear it.
	m_pCurrent = m_pEnd = m_pBuffer;
}

void CHTTPSession::abort()
{
	m_socket.shutdown();
	close();
}

void CHTTPSession::close()
{
	m_socket.close();
}

void CHTTPSession::setException(const CException& exc)
{
	delete m_pException;
	m_pException = exc.clone();
}


void CHTTPSession::clearException()
{
	delete m_pException;
	m_pException = 0;
}

CStreamSocket CHTTPSession::detachSocket()
{
	CStreamSocket oldSocket(m_socket);
	CStreamSocket newSocket;
	m_socket = newSocket;
	return oldSocket;
}

void CHTTPSession::attachSocket(const CStreamSocket& socket)
{
	m_socket = socket;
}

void CHTTPSession::attachSessionData(const CAny& data)
{
	m_data = data;
}

NET_NAMESPACE_END
